﻿using System;
using System.Linq;
using System.Reflection;
using Myotragus.Data.Domain;
using Myotragus.Data.Metadata;
using Myotragus.Data.Util;

namespace Myotragus.Data.Emit
{
	public abstract class EntityGenerator : TypeGenerator, IEntityGenerator
	{
		#region Fields, Consts && Properties

		protected static readonly Type TypeOfIEntity = typeof(IEntity);

		protected static readonly Type TypeOfGeneratedEntity = typeof(GeneratedEntityAttribute);
		protected static readonly ConstructorInfo GeneratedEntityConstructor = TypeOfGeneratedEntity.GetConstructors().First();

		protected INameGenerator NameGenerator
		{
			get;
			private set;
		}

		#endregion

		#region Public Members

		protected EntityGenerator(IDynamicAssembly dynamicAssembly, INameGenerator nameGenerator)
			: base(dynamicAssembly)
		{
			NameGenerator = nameGenerator;
		}

		protected override IDynamicType GenerateImplementation(Type interfaceType)
		{
			var type = CreateType(interfaceType);

			type.AddInterfaceImplementation(TypeOfIEntity);
			type.DefineDefaultConstructor(MethodAttributes.Public);

			AttachImplementation(type, interfaceType);

			return type;
		}

		private IDynamicType CreateType(Type interfaceType)
		{
			var name = NameGenerator.CreateTypeName(DynamicAssembly.MainNamespace,
				interfaceType.Name);

			return DefineType(name,
				TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass
				| TypeAttributes.Public,
				ReflectionHelper.TypeOfObject, new[] { interfaceType });
		}

		#endregion

		#region Protected Tools

		protected abstract void AttachImplementation(IDynamicType type, Type interfaceType);

		#endregion
	}
}